module("framework.mvc.view.BaseViewContainer", package.seeall)

---@class BaseViewContainer : UserDataDispose
local BaseViewContainer = class("BaseViewContainer", UserDataDispose)

BaseViewContainer.Status_None = 0
BaseViewContainer.Status_ResLoading = 1
BaseViewContainer.Status_Opening = 2
BaseViewContainer.Status_Open = 3
BaseViewContainer.Status_Closing = 4
BaseViewContainer.Status_Close = 5

BaseViewContainer.Stage_onOpen = 1
BaseViewContainer.Stage_onOpenFinish = 2
BaseViewContainer.Stage_onClose = 3
BaseViewContainer.Stage_onCloseFinish = 4

BaseViewContainer.ViewLoadingCount = 0

BaseViewContainer.CloseTypeManual = 1

local openingKey = "view opening"

function BaseViewContainer:ctor()
    LuaEventSystem.addEventMechanism(self)
    self._views = nil
    self._tabViews = nil
    self._viewSetting = nil
    self._isOpenImmediate = false
    self._isCloseImmediate = false
    self._viewStatus = BaseViewContainer.Status_None
    self._isVisible = true
    self._abLoader = nil
    self._canvasGroup = nil
    self.viewName = nil
    self.viewParam = nil
    self.viewGO = nil
    self._closeType = nil --需要知道界面是如何关闭的，比如是否手动关闭
end

function BaseViewContainer:setCloseType(type)
    self._closeType = type
end

function BaseViewContainer:isManualClose()
    return self._closeType == BaseViewContainer.CloseTypeManual
end

---@param viewSetting ViewDefine
function BaseViewContainer:setSetting(viewName, viewSetting)
    self.viewName = viewName
    self._viewSetting = viewSetting
end

function BaseViewContainer:getSetting()
    return self._viewSetting
end

-- 打开界面，内部接口，给ViewMgr调用
function BaseViewContainer:openInternal(viewParam, isImmediate)
    self.viewParam = viewParam
    self._isOpenImmediate = isImmediate
    if self._viewStatus == BaseViewContainer.Status_ResLoading or
        self._viewStatus == BaseViewContainer.Status_Opening or
        self._viewStatus == BaseViewContainer.Status_Open then
        return
    end
    ViewMgr.instance:dispatchEvent(ViewEvent.BeforeOpenView, self.viewName, self.viewParam)
    if not self._abLoader then
        self:_setVisible(true)
        self._viewStatus = BaseViewContainer.Status_ResLoading
        self:_loadViewRes()
        return
    end
    -- 界面正在关闭，又被打开了，先走完关闭流程，再次调用打开
    if self._viewStatus == BaseViewContainer.Status_Closing then
        self:onPlayCloseTransitionFinish()
    end
    self:_setVisible(true)
    self:_reallyOpen()
end

-- 关闭界面，内部接口，给ViewMgr调用
function BaseViewContainer:closeInternal(isImmediate)
    self._isCloseImmediate = isImmediate
    if self._viewStatus == BaseViewContainer.Status_ResLoading then
        --加载资源过程中，关闭界面，则取消资源的回调，回到初始状态
        if self._abLoader then
            self._abLoader:dispose()
            self._abLoader = nil
            BaseViewContainer.ViewLoadingCount = BaseViewContainer.ViewLoadingCount - 1
            if BaseViewContainer.ViewLoadingCount <= 0 then
                UIBlockMgr.instance:endBlock(openingKey)
            end
        end
        self._viewStatus = BaseViewContainer.Status_None
    elseif self._viewStatus == BaseViewContainer.Status_Closing then
        --界面关闭过程中，再次调用关闭界面，是否需要立刻关闭
        if isImmediate then
            self:onPlayCloseTransitionFinish()
        end
    elseif self._viewStatus == BaseViewContainer.Status_Opening then
        --界面打开过程中，关闭界面，立刻走完打开流程，转到关闭流程去
        self:onPlayOpenTransitionFinish()
        self:_reallyClose()
    elseif self._viewStatus == BaseViewContainer.Status_Open then
        self:_reallyClose()
    end
end

--设置显示or隐藏viewGO，框架内部使用，禁止外部使用，以免出bug
function BaseViewContainer:setVisibleInternal(isVisible)
    self:_setVisible(isVisible)
end

-- 更新参数，刷新界面
function BaseViewContainer:onUpdateParamInternal(viewParam)
    self:_setVisible(true)
    self.viewParam = viewParam
    if self._views then
        for _, item in ipairs(self._views) do
            item.viewParam = viewParam
            item:onUpdateParamInternal()
        end
    end
    self:onContainerUpdateParam()
end

-- 点击了模态框的Mask，内部接口，给ViewMgr调用
function BaseViewContainer:onClickModalMaskInternal()
    if self._views then
        for _, item in ipairs(self._views) do
            item:onClickModalMaskInternal()
        end
    end
    self:onContainerClickModalMask()
end

function BaseViewContainer:isOpen()
    return self:isOpening() or self:isOpenFinish()
end

function BaseViewContainer:isOpening()
    return self._viewStatus == BaseViewContainer.Status_ResLoading or self._viewStatus == BaseViewContainer.Status_Opening
end

function BaseViewContainer:isOpenFinish()
    return self._viewStatus == BaseViewContainer.Status_Open
end

function BaseViewContainer:isClosing()
    return self._viewStatus == BaseViewContainer.Status_Closing
end

function BaseViewContainer:destroyView()
    if self._views then
        for _, item in ipairs(self._views) do
            item:removeEventsInternal()
            item:onDestroyViewInternal()
            --item:__onDispose()
            item:tryCallMethodName("__onDispose") --try catch处理 2025-1-22
        end
    end
    if self.viewGO then
        gohelper.destroy(self.viewGO)
        self.viewGO = nil
    end
    if self._abLoader then
        self._abLoader:dispose()
        self._abLoader = nil
    end
    self:onContainerDestroy()
end

--关闭自己
function BaseViewContainer:closeThis()
    ViewMgr.instance:closeView(self.viewName)
end

function BaseViewContainer:getRes(resPath)
    local assetItem = self._abLoader:getAssetItem(resPath)
    if assetItem then
        return assetItem:GetResource(resPath)
    end
    return nil
end

function BaseViewContainer:getResInst(resPath, parentGO, name)
    local assetItem = self._abLoader:getAssetItem(resPath)
    if assetItem then
        local prefab = assetItem:GetResource(resPath)
        if prefab then
            return gohelper.clone(prefab, parentGO, name)
        else
            logError(self.__cname .. " prefab not exist: " .. resPath)
        end
    else
        logError(self.__cname .. " resource not load: " .. resPath)
    end
    return nil
end

function BaseViewContainer:_loadViewRes()
    local mainResPath = self._viewSetting.mainRes
    local otherResPath = self._viewSetting.otherRes
    local allResPath = {mainResPath}
    if otherResPath then
        for _, k in pairs(otherResPath) do
            table.insert(allResPath, k)
        end
    end

    -- 需要预加载的资源
    self:preloadRes(allResPath)
    
    --2020-12-18 加载打开关闭动画资源
    if not string.nilorempty(self._viewSetting.anim) and string.find(self._viewSetting.anim, ".controller") then
        table.insert(allResPath, self._viewSetting.anim)
    end
    
    BaseViewContainer.ViewLoadingCount = BaseViewContainer.ViewLoadingCount + 1
    UIBlockMgr.instance:startBlock(openingKey)
    self._abLoader = MultiAbLoader.New()
    self._abLoader:setPathList(allResPath)
    self._abLoader:startLoad(self._onResLoadFinish, self)
end

function BaseViewContainer:_onResLoadFinish(multiAbLoader)
    local uiLayerGO = ViewMgr.instance:getUILayer(self._viewSetting.layer)
    local assetItem = self._abLoader:getAssetItem(self._viewSetting.mainRes)
    local mainPrefab = assetItem:GetResource(self._viewSetting.mainRes)
    self.viewGO = gohelper.clone(mainPrefab, uiLayerGO, self.viewName)
    self._views = self:buildViews()
    if self._views then
        for _, item in ipairs(self._views) do
            --item:__onInit()
            item:tryCallMethodName("__onInit")  --try catch处理 2025-1-22
            item.viewGO = self.viewGO
            item.viewContainer = self
            item.viewName = self.viewName
            item:onInitViewInternal()
            item:addEventsInternal()
        end
    end
    self:onContainerInit()
    BaseViewContainer.ViewLoadingCount = BaseViewContainer.ViewLoadingCount - 1
    if BaseViewContainer.ViewLoadingCount <= 0 then
        UIBlockMgr.instance:endBlock(openingKey)
    end
    self:_reallyOpen()
end

--显示和隐藏，不调用setActive（容易卡顿），隐藏时将界面移到屏幕外，同时把alpha设为0（这个操作能把View的DC降为0）
function BaseViewContainer:_setVisible(isVisible)
    self._isVisible = isVisible

    --战斗返回副本界面会同时打开多个全屏界面，未加载完成的BaseViewContainer获取不到viewGO
    if not self.viewGO then
        return
    end

    if not self._canvasGroup then
        self._canvasGroup = gohelper.onceAddComponent(self.viewGO, typeof(UnityEngine.CanvasGroup))
    end

    if isVisible then
        self._canvasGroup.alpha = 1
        self._canvasGroup.interactable = true
        self._canvasGroup.blocksRaycasts = true
        recthelper.setAnchorX(self.viewGO.transform, 0)
    else
        self._canvasGroup.alpha = 0
        self._canvasGroup.interactable = false
        self._canvasGroup.blocksRaycasts = false
        recthelper.setAnchorX(self.viewGO.transform, 10000)
    end
end

function BaseViewContainer:_reallyOpen()
    self.viewGO.transform:SetAsLastSibling()
    self:_setVisible(self._isVisible)
    self._viewStatus = BaseViewContainer.Status_Opening
    self:_onViewStage(BaseViewContainer.Stage_onOpen)
    if self._viewStatus ~= BaseViewContainer.Status_Opening then
        if isDebugBuild then
            logError(self.viewName .. " status change while opening " .. self._viewStatus)
        end
        return
    end
    if self._isOpenImmediate then
        self:onPlayOpenTransitionFinish()
    else
        self:playOpenTransition()
    end
end

function BaseViewContainer:_reallyClose()
    self._viewStatus = BaseViewContainer.Status_Closing
    self:_onViewStage(BaseViewContainer.Stage_onClose)
    if self._viewStatus ~= BaseViewContainer.Status_Closing then
        if isDebugBuild then
            logError(self.viewName .. " status change while closing " .. self._viewStatus)
        end
        return
    end
    if self._isCloseImmediate then
        self:onPlayCloseTransitionFinish()
    else
        self:playCloseTransition()
    end
end

--处理子View的各个阶段，以及各个阶段的事件派发
function BaseViewContainer:_onViewStage(viewStage)
    if self._views then
        for _, item in ipairs(self._views) do
            if viewStage == BaseViewContainer.Stage_onOpen then
                item.viewParam = self.viewParam
                item:onOpenInternal()
            elseif viewStage == BaseViewContainer.Stage_onOpenFinish then
                item:onOpenFinishInternal()
            elseif viewStage == BaseViewContainer.Stage_onClose then
                item:onCloseInternal()
            elseif viewStage == BaseViewContainer.Stage_onCloseFinish then
                item:onCloseFinishInternal()
            end
        end
    end
    if viewStage == BaseViewContainer.Stage_onOpen then
        ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenView, self.viewName, self.viewParam)
        if self._viewSetting.viewType == ViewType.Full then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenFullView, self.viewName, self.viewParam)
        elseif self._viewSetting.viewType == ViewType.Modal then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenModalView, self.viewName, self.viewParam)
        end
        self:onContainerOpen()
    elseif viewStage == BaseViewContainer.Stage_onOpenFinish then
        ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenViewFinish, self.viewName, self.viewParam)
        if self._viewSetting.viewType == ViewType.Full then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenFullViewFinish, self.viewName, self.viewParam)
        elseif self._viewSetting.viewType == ViewType.Modal then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnOpenModalViewFinish, self.viewName, self.viewParam)
        end
        self:onContainerOpenFinish()
    elseif viewStage == BaseViewContainer.Stage_onClose then
        ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseView, self.viewName, self.viewParam)
        if self._viewSetting.viewType == ViewType.Full then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseFullView, self.viewName, self.viewParam)
        elseif self._viewSetting.viewType == ViewType.Modal then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseModalView, self.viewName, self.viewParam)
        end
        self:onContainerClose()
    elseif viewStage == BaseViewContainer.Stage_onCloseFinish then
        ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseViewFinish, self.viewName, self.viewParam)
        if self._viewSetting.viewType == ViewType.Full then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseFullViewFinish, self.viewName, self.viewParam)
        elseif self._viewSetting.viewType == ViewType.Modal then
            ViewMgr.instance:dispatchEvent(ViewEvent.OnCloseModalViewFinish, self.viewName, self.viewParam)
        end
        self:onContainerCloseFinish()
    end
end

function BaseViewContainer:getTabContainer(tabContainerId)
    return self._tabViews and self._tabViews[tabContainerId]
end

-- 是否存在异常的调度 2025-1-22
function BaseViewContainer:isHasTryCallFail()
    if self._views then
        for _, item in ipairs(self._views) do
            if item:isHasTryCallFail() then 
                return true 
            end
        end
    end
    return false
end

--可重写方法，播放界面打开的过渡动画，播放结束需要显式调用 onPlayOpenTransitionFinish
function BaseViewContainer:playOpenTransition()
    self:onPlayOpenTransitionFinish()
end

function BaseViewContainer:onPlayOpenTransitionFinish()
    self._viewStatus = BaseViewContainer.Status_Open
    self:_onViewStage(BaseViewContainer.Stage_onOpenFinish)
end

--可重写方法，播放界面关闭的过渡动画，播放结束需要显式调用 onPlayCloseTransitionFinish
function BaseViewContainer:playCloseTransition()
    self:onPlayCloseTransitionFinish()
end

function BaseViewContainer:onPlayCloseTransitionFinish()
    self:_setVisible(false)
    self._viewStatus = BaseViewContainer.Status_Close
    self:_onViewStage(BaseViewContainer.Stage_onCloseFinish)
    self:setCloseType(nil)
end

--可重写方法，android手机按下返回键时，对界面的处理
function BaseViewContainer:onAndroidBack()
end

--需重写方法，构建子界面（继承自 BaseView ），返回子界面实例列表
-- e.g. return {View1.New(), View2.New()}
function BaseViewContainer:buildViews()
end

--TabView需要重写获取子TabView的实例
-- e.g. if tabContainerId == 1 then
--          return {View1.New(), View2.New()}
--      end
function BaseViewContainer:buildTabViews(tabContainerId)
end

--可重写方法，界面初始化
function BaseViewContainer:onContainerInit()
end

--可重写方法，界面销毁
function BaseViewContainer:onContainerDestroy()
end

--可重写方法，界面打开阶段
function BaseViewContainer:onContainerOpen()
end

--可重写方法，界面打开完成
function BaseViewContainer:onContainerOpenFinish()
end

--可重写方法，界面关闭阶段
function BaseViewContainer:onContainerClose()
end

--可重写方法，界面关闭完成
function BaseViewContainer:onContainerCloseFinish()
end

--可重写方法，界面打开状态下，再次打开界面，则更新参数
function BaseViewContainer:onContainerUpdateParam()
end

--可重写方法，点击了模态框的黑色透明遮罩
function BaseViewContainer:onContainerClickModalMask()
end

--可重写方法，设置需要预加载的资源
function BaseViewContainer:preloadRes(allRes)
    
end

return BaseViewContainer
